import { Meta, Canvas, Source } from '@storybook/addon-docs'
import { createTitle, COOKBOOK } from '../config'
import nextJsThemeExampleGif from './assets/next-js-theme-example.gif'

<Meta title={createTitle([COOKBOOK.root, COOKBOOK.nextjs])} />

# Использование @v-uik в NextJS приложении

Компоненты `@v-uik` проверены в NextJS@^13.x.x и NextJS@^14.x.x

## Перед началом работы

Требуется отключить React.StrictMode в `next.config.js`, так как для стилизации используется jss.
На текущий момент он не поддерживает React.StrictMode при SSR

```ts
/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: false,
}

module.exports = nextConfig
```

## NextJS версии ^13.0.0 с использованием `App Router`

### Настройка

#### layout.tsx

Для корректной стилей в `App Router` вам необходимо обернуть приложение в корневом `layout.tsx` в провайдер `NextJsProvider` из пакета `@v-uik/next-js-provider`.
Данный пакет требует установку `Next JS` версии `13.0.0` и выше.

```ts
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import { NextJsProvider } from '@v-uik/next-js-provider'
import { ThemeProvider, createTheme } from '@v-uik/theme'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export const theme = createTheme({
  sys: {
    color: {
      primaryAlpha: 'red',
    },
  },
  comp: {
    backwardCompatibilityMode: false,
  },
})

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <html lang="ru">
      <body className={inter.className}>
        <NextJsProvider>
          <ThemeProvider theme={theme}>{children}</ThemeProvider>
        </NextJsProvider>
      </body>
    </html>
  )
}
```

### Динамическое изменение темы

Пример с динамическим изменением светлой и темной темы по нажатию кнопки

#### CustomThemeProvider.tsx

Создаем провайдер темы из библиотеки с возможностью изменения значения темы через контекст `CustomThemeContext`

```ts
'use client'

import React from 'react'
import { ThemeProvider, Theme, light, dark, createTheme } from '@v-uik/base'

const THEMES: Record<ThemeMode, Theme> = {
  light: createTheme(light),
  dark: createTheme(dark),
}

export type ThemeMode = 'light' | 'dark'

export type CustomThemeContextProps = [ThemeMode, (theme: ThemeMode) => void]

export type CustomThemeProviderProps = React.FC<React.PropsWithChildren>

export const CustomThemeContext = React.createContext<CustomThemeContextProps>([
  '' as ThemeMode,
  () => {},
])

export const CustomThemeProvider: CustomThemeProviderProps = ({ children }) => {
  const [theme, setTheme] = React.useState<ThemeMode>(
    () => () =>
      typeof localStorage === 'undefined'
        ? 'light'
        : (localStorage?.getItem('theme') as ThemeMode) || 'light'
  )

  const handleChangeTheme = (theme: ThemeMode) => {
    localStorage.setItem('theme', theme)
    setTheme(theme)
  }

  return (
    <CustomThemeContext.Provider value={[theme, handleChangeTheme]}>
      <ThemeProvider theme={THEMES[theme]}>{children}</ThemeProvider>
    </CustomThemeContext.Provider>
  )
}
```

#### ThemeSwitcher.tsx

Создаем компонент для переключения темы по нажатию кнопки

```ts
'use client'

import React from 'react'
import { CustomThemeContext } from './CustomThemeProvider'
import { Button, createUseStyles } from '@v-uik/base'

const useStyles = createUseStyles((theme) => ({
  background: {
    background: theme.sys.color.backgroundAlpha,
    border: '1px solid #000',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    height: 200,
    width: 200,
  },
}))

export const ThemeSwitcher = () => {
  const [theme, setTheme] = React.useContext(CustomThemeContext)
  const classes = useStyles()

  const handleChangeTheme = () => {
    setTheme(theme === 'light' ? 'dark' : 'light')
  }

  return (
    <div className={classes.background}>
      <Button onClick={handleChangeTheme} color="error">
        Change Theme ({theme})
      </Button>
    </div>
  )
}
```

#### layout.tsx

Оборачиваем приложение в `NextJsProvider` и в созданный выше провайдер темы `CustomThemeProvider`

```ts
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import { NextJsProvider } from '@v-uik/next-js-provider'
import { CustomThemeProvider } from './CustomThemeProvider'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <NextJsProvider>
      <CustomThemeProvider>
        <html lang="ru">
          <body className={inter.className}>{children}</body>
        </html>
      </CustomThemeProvider>
    </NextJsProvider>
  )
}
```

#### page.tsx

Отображаем на странице переключатель темы `ThemeSwitcher`

```ts
import { ThemeSwitcher } from './ThemeSwitcher'

export default function Home() {
  return (
    <main>
      <ThemeSwitcher />
    </main>
  )
}
```

Результатом будет являться приложение `Next JS`, которое способно переключать светлую и темную тему по нажатию кнопки

<div
  style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}
>
  <figure>
    <img
      alt="Компонент переключения светлой и темной темы"
      src={nextJsThemeExampleGif}
    />
    <figcaption style={{ textAlign: 'center' }}>
      Компонент переключения светлой и темной темы
    </figcaption>
  </figure>
</div>

## NextJS версии ^12.0.0 с использованием `Page Router`

### Настройка

#### \_document.tsx

Необходимо сгенерировать стили `react-jss` на сервере

```ts
import Document, { DocumentContext } from 'next/document'
import React from 'react'
import { SheetsRegistry, JssProvider, createGenerateId } from 'react-jss'

export default class JssDocument extends Document {
  static async getInitialProps(ctx: DocumentContext) {
    const registry = new SheetsRegistry()
    const generateId = createGenerateId()
    const originalRenderPage = ctx.renderPage
    ctx.renderPage = () =>
      originalRenderPage({
        enhanceApp: (App) => (props) =>
          (
            <JssProvider registry={registry} generateId={generateId}>
              <App {...props} />
            </JssProvider>
          ),
      })

    const initialProps = await Document.getInitialProps(ctx)

    return {
      ...initialProps,
      styles: (
        <>
          {initialProps.styles}
          <style id="server-side-styles">{registry.toString()}</style>
        </>
      ),
    }
  }
}
```

#### \_app.tsx

После загрузки на клиенте серверные стили необходимо удалить. Также требуется добавить
`GeneratedIdProvider` из пакета `@v-uik/hooks` (входит в состав `@v-uik/base`)

```ts
import type { AppProps } from 'next/app'
import { ThemeProvider, light, GeneratedIdProvider } from '@v-uik/base'
import { useEffect } from 'react'
import '../styles/global.css'
import React from 'react'

export default function App({ Component, pageProps }: AppProps) {
  useEffect(() => {
    const style = document.getElementById('server-side-styles')
    if (style && style.parentNode) {
      style.parentNode.removeChild(style)
    }
  }, [])

  return (
    <ThemeProvider theme={light}>
      <GeneratedIdProvider>
        <Component {...pageProps} />
      </GeneratedIdProvider>
    </ThemeProvider>
  )
}
```

## Известные проблемы

Библиотека [react-jss](https://cssinjs.org/react-jss/?v=v10.6.0) при SSR может выводить в консоль `Node JS` следующее предупреждение:

```
Warning: [JSS] Rule is not linked. Missing sheet option "link: true".
```

Это предупреждение описано в [топике](https://github.com/cssinjs/jss/issues/1277) самой библиотеки и оно никак не влияет на работоспособность компонентов `@v-uik` в `Next JS`.

## Полезные ссылки

- [App Router](https://nextjs.org/docs/app/building-your-application/routing#the-app-router);

- [Серверные компоненты Next JS](https://nextjs.org/docs/app/building-your-application/rendering/server-components);

- [Pages Router](https://nextjs.org/docs/pages).
